import json
import os
import re
from datetime import datetime,date, timedelta
import shutil
import socket
import time
from typing import List, Optional, Tuple, cast  # noqa: F401
import jieba
from flask import abort, current_app, g, jsonify, make_response, redirect, render_template, request
from werkzeug.wrappers import Response
import requests
import flask_calendar.constants as constants
from flask_calendar.app_utils import (
    add_session,
    authenticated,
    authorized,
    get_session_username,
    new_session_id,
    next_month_link,
    previous_month_link,
)
from flask_calendar.trans_lunar import show_lunar,show_lunar_year
from flask_calendar.authentication import Authentication
from flask_calendar.calendar_data import CalendarData
from flask_calendar.gregorian_calendar import GregorianCalendar
from flask_calendar.show_weather import ip_located_weather , is_internet
from flask_calendar.date_calculate import show_TeachingPeriod
from flask import Flask
from flask_calendar.sql_function import sql_query
search_result=[]
search_result_sum=0
app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = 'tmp'
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
app.config['JSON_AS_ASCII']=False
def cons_host_ip():  #等待修改
    ip=str(request.remote_addr)  # 访问者ip
    if is_internet(ip):
        ip_ip=requests.get('http://myip.ipip.net', timeout=5).text
        return ip_ip.split(":")[1].split("  ")[0]
        pass
    else:
        ip_list=[]
        ip_list=ip.split(".")
        if ip_list == ["127","0","0","1"] or ip_list == ["0","0","0","0"]:
            return ip
            pass
        else:
            return socket.gethostbyname(socket.gethostname())
            pass
        pass
    # res = requests.get('http://myip.ipip.net', timeout=5).text

    return 


def get_authentication() -> Authentication:
    auth = getattr(g, "_auth", None)
    if auth is None:
        auth = g._auth = Authentication(
            data_folder=current_app.config["USERS_DATA_FOLDER"],
            password_salt=current_app.config["PASSWORD_SALT"],
            failed_login_delay_base=current_app.config["FAILED_LOGIN_DELAY_BASE"],
        )
    return cast(Authentication, auth)


@authenticated
def index_action() -> Response:
    
    username = get_session_username(session_id=str(request.cookies.get(constants.SESSION_ID)))
    # 用户名
    authentication = get_authentication()
    # 
    user_data = authentication.user_data(username)
    return redirect("/{}/".format(user_data["default_calendar"]))
    # 就从此处路由传参的 传到了 calendar_id 中


def login_action() -> Response:
    return cast(Response, render_template("login.html"))


def do_login_action() -> Response:
    username = request.form.get("username", "")
    password = request.form.get("password", "")
    authentication = get_authentication()

    if authentication.is_valid(username, password):
        session_id = new_session_id()
        add_session(session_id, username)
        response = make_response(redirect("/"))

        cookie_kwargs = {
            "key": constants.SESSION_ID,
            "value": session_id,
            # 1 month
            "max_age": 2678400,
            "secure": current_app.config["COOKIE_HTTPS_ONLY"],
            "httponly": True,
        }

        samesite_policy = current_app.config.get("COOKIE_SAMESITE_POLICY", None)
        # Certain Flask versions don't support 'samesite' param
        if samesite_policy:
            cookie_kwargs.update({"samesite": samesite_policy})

        response.set_cookie(**cookie_kwargs)
        return cast(Response, response)
    else:
        return redirect("/login")


@authenticated
@authorized
def main_calendar_action(calendar_id: str) -> Response:
    global search_result
    global search_result_sum
    # print("jkjkjk")
    # print(search_result)
    # if search_result!=((),): # 刷新后消失，刷新不会导致重新渲染页面（这个函数再执行一次），所以无效，得在前端处理
    #     search_result_sum+=1
    #     if search_result_sum == 2:
    #         search_result_sum=0
    #     pass
    ip = request.remote_addr
    # print("jjj")
    # print(ip_located_weather(ip))
    GregorianCalendar.setfirstweekday(current_app.config["WEEK_STARTING_DAY"])
    current_day, current_month, current_year = GregorianCalendar.current_date()
    year = int(request.args.get("y", current_year))
    # 从路由的？后面获取y信息，应该刚开始是没有的。
    # 有个特性：default – 如果指定键的值不存在时，返回该默认值值
    year = max(min(year, current_app.config["MAX_YEAR"]), current_app.config["MIN_YEAR"])
    month = int(request.args.get("m", current_month))
    month = max(min(month, 12), 1)
    # 确保month在1到12之间。。唔 如果是15，最后出来就是12咯
    month_name = GregorianCalendar.MONTH_NAMES[month - 1]
    # MONTH_NAMES是存放月份名称的数组，按照下标来取的
    lunar={}
    # 这个改成字典来做了，因为前端的语言比较低端 不好处理数组
    if current_app.config["HIDE_PAST_TASKS"]:
        view_past_tasks = False
    else:
        view_past_tasks = request.cookies.get("ViewPastTasks", "1") == "1"

    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])
    # 给出data文件夹这些的，目的是为了task 的分布确定
    try:
        data = calendar_data.load_calendar(calendar_id) # 读取json
    except FileNotFoundError:
        abort(404)
    # print(88888888888)
    # print(data)
    # print(88888888888)
    tasks = calendar_data.tasks_from_calendar(year, month, data)
    # task_sql = calendar_data.tasks_from_calendar(year, month, data)
    timer_normal = calendar_data.add_timer_normal(str(current_year)+"-"+str(current_month)+"-"+str(current_day),year, month, data)
    print(timer_normal)
    tasks = calendar_data.add_repetitive_tasks_from_calendar(year, month, data, tasks) # data 作为一个字典，传参时是地址传递，在此函数中被改变了
    # timer_rep = calendar_data.add_timer(str(current_year)+"-"+str(current_month)+"-"+str(current_day),year, month, data)
    # print(timer)
    if not view_past_tasks:
        calendar_data.hide_past_tasks(year, month, tasks)

    if current_app.config["WEEK_STARTING_DAY"] == constants.WEEK_START_DAY_MONDAY:
        weekdays_headers = ["星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"]
    else:
        weekdays_headers = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"]
    # 排列不同
    lunar_year=show_lunar_year(str(current_year)+"-"+str(current_month)+"-"+str(current_day))
    ## 课程表
    try:
        with open("table.json",encoding='utf-8') as file:
            table_info = json.load(file)
        today_table=table_info[weekdays_headers[datetime(int(current_year),int(current_month),int(current_day)).weekday()]]
    except:
        today_table="没有获取到课表"
    if today_table=={}:
        today_table="今日无课"

    for i in GregorianCalendar.month_days(year, month): 
        lunar[i]=(show_lunar(str(i)))
        # print(show_lunar(str(i)))
        # print(type(i))  # <class 'datetime.date'>
    #     #2022-08-29
    #     #2022-08-30
    #     #2022-08-31
    #     #2022-09-01
    #     #2022-09-02
    #     #2022-09-03
    #     #....
        # 对于提醒功能，如果是重复的，就提醒最近一次，其他的不论是什么时间的，都给出倒计时，如果时间正好到了。，标红 而且发送到邮箱中
    return cast(
        Response,
        render_template(
            "calendar.html",
            calendar_id=calendar_id,
            year=year,
            month=month,
            month_name=month_name,
            current_year=current_year,
            current_month=current_month,
            current_day=current_day,
            month_days=GregorianCalendar.month_days(year, month),
            month_dayss=GregorianCalendar.month_days(year, month),
            previous_month_link=previous_month_link(year, month),
            next_month_link=next_month_link(year, month),
            base_url="http://"+cons_host_ip() +":5000",
            tasks=tasks,
            taskss=tasks,
            display_view_past_button=current_app.config["SHOW_VIEW_PAST_BUTTON"],
            weekdays_headers=weekdays_headers,
            lunar=lunar,
            lunar_year=lunar_year,
            weather=ip_located_weather(ip),
            week=show_TeachingPeriod(str(current_year)+"-"+str(current_month)+"-"+str(current_day)),
            timer_normal=timer_normal,
            week_day=weekdays_headers[datetime(int(current_year),int(current_month),int(current_day)).weekday()],
            table=today_table,
            search_result=search_result,
        ),
    )


@authenticated
@authorized
def new_task_action(calendar_id: str, year: int, month: int) -> Response:  # new只是new，不对数据做操作  但是实在这个页面上
    
    GregorianCalendar.setfirstweekday(current_app.config["WEEK_STARTING_DAY"])

    current_day, current_month, current_year = GregorianCalendar.current_date()
    year = max(min(int(year), current_app.config["MAX_YEAR"]), current_app.config["MIN_YEAR"])
    month = max(min(int(month), 12), 1)
    month_names = GregorianCalendar.MONTH_NAMES

    if current_month == month and current_year == year:
        day = current_day
    else:
        day = 1
    day = int(request.args.get("day", day))

    task = {
        "date": CalendarData.date_for_frontend(year, month, day),
        "is_all_day": True,
        "repeats": False,
        "details": "",
    }

    emojis_enabled = current_app.config.get("EMOJIS_ENABLED", False)

    return cast(
        Response,
        render_template(
            "task.html",
            calendar_id=calendar_id,
            year=year,
            month=month,
            min_year=current_app.config["MIN_YEAR"],
            max_year=current_app.config["MAX_YEAR"],
            month_names=month_names,
            task=task,
            base_url="http://"+cons_host_ip() +":5000",
            editing=False,
            emojis_enabled=emojis_enabled,
            button_default_color_value=current_app.config["BUTTON_CUSTOM_COLOR_VALUE"],
            buttons_colors=current_app.config["BUTTONS_COLORS_LIST"],
            # buttons_emojis=current_app.config["BUTTONS_EMOJIS_LIST"] if emojis_enabled else tuple(),
        ),
    )


@authenticated
@authorized
def edit_task_action(calendar_id: str, year: int, month: int, day: int, task_id: int) -> Response: # edit只是edit，不做编辑
    month_names = GregorianCalendar.MONTH_NAMES
    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])

    repeats = request.args.get("repeats") == "1"
    try:
        if repeats:
            task = calendar_data.repetitive_task_from_calendar(
                calendar_id=calendar_id, year=year, month=month, task_id=int(task_id)
            )
        else:
            task = calendar_data.task_from_calendar(
                calendar_id=calendar_id,
                year=year,
                month=month,
                day=day,
                task_id=int(task_id),
            )
    except (FileNotFoundError, IndexError):
        abort(404)

    if task["details"] == "&nbsp;":
        task["details"] = ""

    emojis_enabled = current_app.config.get("EMOJIS_ENABLED", False)

    return cast(
        Response,
        render_template(
            "task.html",
            calendar_id=calendar_id,
            year=year,
            month=month,
            day=day,
            min_year=current_app.config["MIN_YEAR"],
            max_year=current_app.config["MAX_YEAR"],
            month_names=month_names,
            task=task,
            base_url="http://"+cons_host_ip() +":5000",
            editing=True,
            emojis_enabled=emojis_enabled,
            button_default_color_value=current_app.config["BUTTON_CUSTOM_COLOR_VALUE"],
            buttons_colors=current_app.config["BUTTONS_COLORS_LIST"],
            # buttons_emojis=current_app.config["BUTTONS_EMOJIS_LIST"] if emojis_enabled else tuple(),
        ),
    )


@authenticated
@authorized
def update_task_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:    # 动作
    # Logic is same as save + delete, could refactor but can wait until need to change any save/delete logic
    # print("upload下")
    # print(request)
    # print(request.form)
    # print("上")
    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])
    file = request.files['file1']
    filename=file.filename
    # print(filename)
    if(filename!=""):
        now_time="_"+str(int(time.time()))
        filename_all=filename.split('.')[0]+now_time+'.'+filename.split('.')[1]
        # 判断一下文件名合法，要有拓展名
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename_all))
        # fileurl = request.form['file1']
        # print(fileurl)
        url_file="/static/files/"+filename_all
        shutil.move("tmp/"+filename_all,'flask_calendar/static/files/'+filename_all)
        # print("url_fileurl_fileurl_fileurl_file")
        # print(url_file)
    else:
        # print("null")
        url_file=''
    # For creation of "updated" task use only form data
    title = request.form["title"].strip()
    start_date = request.form.get("date", "")
    if len(start_date) > 0:
        fragments = re.split("-", start_date)
        updated_year = int(fragments[0])  # type: Optional[int]
        updated_month = int(fragments[1])  # type: Optional[int]
        updated_day = int(fragments[2])  # type: Optional[int]
    else:
        updated_year = updated_month = updated_day = None
    is_all_day = request.form.get("is_all_day", "0") == "1"
    start_time = request.form["start_time"]
    end_time = request.form.get("end_time", None)
    details = request.form["details"].replace("\r", "").replace("\n", "<br>")
    color = request.form["color"]
    has_repetition = request.form.get("repeats", "0") == "1"
    repetition_type = request.form.get("repetition_type", "")
    repetition_subtype = request.form.get("repetition_subtype", "")
    repetition_value = int(request.form["repetition_value"])  # type: int
    # url_file=request.form.get("fileurl", "")   # 已替换
    # if url_file== "None":
    #     url_file=''
    calendar_data.create_task(
        calendar_id=calendar_id,
        year=updated_year,
        month=updated_month,
        day=updated_day,
        title=title,
        is_all_day=is_all_day,
        start_time=start_time,
        end_time=end_time,
        details=details,
        color=color,
        has_repetition=has_repetition,
        repetition_type=repetition_type,
        repetition_subtype=repetition_subtype,
        repetition_value=repetition_value,
        url_file=url_file,
    )
    # For deletion of old task data use only url data
    calendar_data.delete_task(
        calendar_id=calendar_id,
        year_str=year,
        month_str=month,
        day_str=day,
        task_id=int(task_id),
    )

    if updated_year is None:
        return redirect("{}/{}/".format("http://"+cons_host_ip() +":5000", calendar_id), code=302)
    else:
        return redirect(
            "{}/{}/?y={}&m={}".format("http://"+cons_host_ip() +":5000", calendar_id, updated_year, updated_month),
            code=302,
        )


@authenticated
@authorized
def search(calendar_id: str) -> Response:
    global search_result
    # print(121)
    if  request.method == 'POST':
        # print(4555555555)
        print(request.form)
        keyword=request.form.get('keyword')
        cut_keywords = jieba.cut_for_search(keyword)
        # search_result = []
        # 取出待搜索keyword
        keyword = request.form['keyword']
        search_result.clear()
        # 对keyword分词
        cut_keywords = jieba.cut_for_search(keyword)
        for cut_keyword in cut_keywords:
            # print(cut_keyword)
            search_result+=((sql_query(cut_keyword)))   # 通过全局变量把值传出去
            # print(search_result)
            # 正确的
        search_result=(list(set(search_result)))
    print("http://"+cons_host_ip() +":5000")
    return redirect("{}/{}/".format("http://"+cons_host_ip() +":5000", calendar_id), code=302)
    # return render_template('search.html')



@authenticated
@authorized
def save_task_action(calendar_id: str) -> Response:    # 动作
    # print("下")
    # print(request)
    # print(request.form)
    # print("上")
    title = request.form["title"].strip()
    startdate = request.form.get("date", "")
    enddate = request.form.get("enddate", "")
    # print(107887878)
    if len(startdate) > 0:
        date_fragments = re.split("-", startdate)
        year = int(date_fragments[0])  # type: Optional[int]
        month = int(date_fragments[1])  # type: Optional[int]
        day = int(date_fragments[2])  # type: Optional[int]
    else:
        year = month = day = None
    # print(request.files)
    file = request.files['file1']
    filename=file.filename
    # print(filename)
    if(filename!=""):
        now_time="_"+str(int(time.time()))
        filename_all=filename.split('.')[0]+now_time+'.'+filename.split('.')[1]
        # 判断一下文件名合法，要有拓展名
        file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename_all))
        # fileurl = request.form['file1']
        # print(fileurl)
        url_file="/static/files/"+filename_all
        shutil.move("tmp/"+filename_all,'flask_calendar/static/files/'+filename_all)
        # print("url_fileurl_fileurl_fileurl_file")
        # print(url_file)
    else:
        # print("null")
        url_file=''
    is_all_day = request.form.get("is_all_day", "0") == "1"
    start_time = request.form["start_time"]
    end_time = request.form.get("end_time", None)
    details = request.form["details"].replace("\r", "").replace("\n", "<br>")
    color = request.form["color"]
    has_repetition = request.form.get("repeats", "0") == "1"
    repetition_type = request.form.get("repetition_type")
    repetition_subtype = request.form.get("repetition_subtype")
    repetition_value = int(request.form["repetition_value"])

    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])

    dates_to_create = []  # type: List[Tuple[Optional[int], Optional[int], Optional[int]]]

    # repetitive tasks not supported
    if startdate != enddate and not has_repetition:
        startdate_fragments = re.split("-", startdate)
        enddate_fragments = re.split("-", enddate)
        sdate = date(int(startdate_fragments[0]), int(startdate_fragments[1]), int(startdate_fragments[2]))
        edate = date(int(enddate_fragments[0]), int(enddate_fragments[1]), int(enddate_fragments[2]))
        delta = edate - sdate
        for i in range(delta.days + 1):
            currentdate = re.split("-", str(sdate + timedelta(days=i)))

            year = int(currentdate[0])
            month = int(currentdate[1])
            day = int(currentdate[2])

            dates_to_create.append((year, month, day))
    else:
        dates_to_create.append((year, month, day))
    # print(100001)
    for date_tuple in dates_to_create:
        year, month, day = date_tuple
        # 使用形式参数传参到create_task中，创建一个task，存在json里的所有信息都是在这创建的。于是也可以在这里多加点东西进行储存。比如文件路径
        calendar_data.create_task(
            calendar_id=calendar_id,
            year=year,
            month=month,
            day=day,
            title=title,
            is_all_day=is_all_day,
            start_time=start_time,
            end_time=end_time,
            details=details,
            color=color,
            has_repetition=has_repetition,
            repetition_type=repetition_type,
            repetition_subtype=repetition_subtype,
            repetition_value=repetition_value,
            url_file=url_file,
        )
    # print(111111111111)
    if year is None:
        print("http://"+cons_host_ip() +":5000")
        return redirect("{}/{}/".format("http://"+cons_host_ip() +":5000", calendar_id), code=302)
        # return redirect("{}/{}/".format(current_app.config["BASE_URL"], calendar_id), code=302) # current_app.config["MY_URL"]
    else:
        print("http://"+cons_host_ip() +":5000")
        return redirect(
            "{}/{}/?y={}&m={}".format("http://"+cons_host_ip() +":5000", calendar_id, year, month),
            # "{}/{}/?y={}&m={}".format(current_app.config["BASE_URL"], calendar_id, year, month),
            code=302,
        )


@authenticated
@authorized
def delete_task_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:  
    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])
    calendar_data.delete_task(
        calendar_id=calendar_id,
        year_str=year,
        month_str=month,
        day_str=day,
        task_id=int(task_id),
    )

    return cast(Response, jsonify({}))


@authenticated
@authorized
def update_task_day_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:
    new_day = request.data.decode("utf-8")

    calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])
    calendar_data.update_task_day(
        calendar_id=calendar_id,
        year_str=year,
        month_str=month,
        day_str=day,
        task_id=int(task_id),
        new_day_str=new_day,
    )

    return cast(Response, jsonify({}))


# @authenticated
# @authorized
# def hide_repetition_task_instance_action(calendar_id: str, year: str, month: str, day: str, task_id: str) -> Response:
#     calendar_data = CalendarData(current_app.config["DATA_FOLDER"], current_app.config["WEEK_STARTING_DAY"])
#     calendar_data.hide_repetition_task_instance(
#         calendar_id=calendar_id,
#         year_str=year,
#         month_str=month,
#         day_str=day,
#         task_id_str=task_id,
#     )

    # return cast(Response, jsonify({}))

# 还没有添加路由，这块是要做一个task的提醒功能
@authenticated
@authorized
def remind_task_action() -> Response:
    pass